home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Games Collection 1 / software vault.zip / software vault / CDR10 / FLIPLAY.ZIP / FLIPLAY.PAS < prev   
Pascal/Delphi Source File  |  1993-02-20  |  33KB  |  686 lines

  1. {$G+}
  2.  
  3. PROGRAM FliPlayer;
  4.  
  5. {  v1.1 made by Thaco   }
  6. { (c) EPOS, August 1992 }
  7.  
  8.  
  9. CONST
  10.   CLOCK_HZ              =4608;                   { Frequency of clock }
  11.   MONITOR_HZ            =70;                     { Frequency of monitor }
  12.   CLOCK_SCALE           =CLOCK_HZ DIV MONITOR_HZ;
  13.  
  14.   BUFFERSIZE            =$FFFE;                  { Size of the framebuffer, must be an even number }
  15.   CDATA                 =$040;                   { Port number of timer 0 }
  16.   CMODE                 =$043;                   { Port number of timers control word }
  17.   CO80                  =$3;                     { Number for standard text mode }
  18.   KEYBOARD              =28;                     { Numbers returned by PORT[$64] indicating what hardware caused INT 09/the - }
  19.   MOUSE                 =60;                     { - number on PORT[$60] }
  20.   MCGA                  =$13;                    { Number for MCGA mode }
  21.   MCGACheck:BOOLEAN     =TRUE;                   { Variable for MCGA checking }
  22.   UseXMS:BOOLEAN        =TRUE;                   { Variable for XMS usage }
  23.   XMSError:BYTE         =0;                      { Variable indicating the errornumber returned from the last XMS operation }
  24.  
  25. TYPE
  26.   EMMStructure          =RECORD
  27.                            BytesToMoveLo,              { Low word of bytes to move. NB: Must be even! }
  28.                            BytesToMoveHi,              { High word of bytes to move }
  29.                            SourceHandle,               { Handle number of source (SH=0 => conventional memory) }
  30.                            SourceOffsetLo,             { Low word of source offset, or OFS if SH=0 }
  31.                            SourceOffsetHi,             { High word of source offset, or SEG if SH=0 }
  32.                            DestinationHandle,          { Handle number of destination (DH=0 => conventional memory) }
  33.                            DestinationOffsetLo,        { Low word of destination offset, or OFS if DH=0 }
  34.                            DestinationOffsetHi  :WORD; { High word of destination offset, or SEG if DH=0 }
  35.                          END;
  36.   HeaderType            =ARRAY[0..128] OF BYTE;  { A buffertype used to read all kinds of headers }
  37.  
  38.  
  39. VAR
  40.   Key,                                           { Variable used to check if a key has been pressed }
  41.   OldKey                :BYTE;                   { Variable used to check if a key has been pressed }
  42.   XMSRecord             :EMMStructure;           { Variable for passing values to the XMS routine }
  43.   InputFile             :FILE;                   { Variable for the incomming .FLI file }
  44.   Header                :HeaderType;             { Buffer used to read all kinds of headers }
  45.   Counter,                                       { General purpose counter }
  46.   Speed                 :INTEGER;                { Timedifference in video tics from one frame to the next }
  47.   FileCounter,                                   { Variable telling the point to read from in the file stored in XMS }
  48.   FileSize,                                      { Size of the .FLI-file }
  49.   FrameSize,                                     { Variable indicating the datasize of current frame }
  50.   NextTime,                                      { Variable saying when it is time to move on to the next frame }
  51.   TimeCounter,                                   { Holding the current time in video tics }
  52.   SecondPos             :LONGINT;                { Number of bytes to skip from the start of the .FLI file when starting - }
  53.                                                  { - from the beginning again }
  54.   Buffer,                                        { Pointer to the Framebuffer }
  55.   XMSEntryPoint         :POINTER;                { Entry point of the XMS routine in memory }
  56.   SpeedString           :STRING[2];              { String used to parse the -sNN command }
  57.   FileName              :STRING[13];             { String holding the name of the .FLI-file }
  58.   BufferHandle,                                  { Handle number returned from the XMS routine }
  59.   BytesRead,                                     { Variable telling the numbers of bytes read from the .FLI file }
  60.   FrameNumber,                                   { Number of the current frame }
  61.   Frames,                                        { Total number of frames }
  62.   Chunks                :WORD;                   { Total number of chunks in a frame }
  63.  
  64.  
  65. FUNCTION UpCaseString(Streng:String):String;
  66. { takes a string and convert all letters to uppercase }
  67. VAR
  68.   DummyString           :String;
  69.   Counter               :Integer;
  70. BEGIN
  71.   DummyString:='';
  72.   FOR Counter:=1 TO Length(Streng) DO
  73.     DummyString:=DummyString+UpCase(Streng[Counter]);
  74.   UpCaseString:=DummyString;
  75. END;
  76.  
  77.  
  78. PROCEDURE InitMode(Mode:WORD); ASSEMBLER;
  79. { uses BIOS interrupts to set a videomode }
  80. ASM
  81.   mov  ax,Mode
  82.   int  10h
  83. END;
  84.  
  85.  
  86. FUNCTION ModeSupport(Mode:WORD):BOOLEAN; ASSEMBLER;
  87. { uses BIOS interrupts to check if a videomode is supported }
  88. LABEL Exit, Last_Modes, No_Support, Supported;
  89. VAR
  90.   DisplayInfo           :ARRAY[1..64] OF BYTE;   { Array for storing functionality/state information }
  91. ASM
  92.   push es
  93.  
  94.   mov  ah,1Bh                                    { the functionality/state information request at int 10h }
  95.   mov  bx,0                                      { 0 = return functionality/state information }
  96.   push ds                                        { push DS on the stack and pop it into ES so ES:DI could be used to - }
  97.   pop  es                                        { - address DisplayInfo, as demanded of the interrupt function }
  98.   mov  di,offset DisplayInfo
  99.   int  10h
  100.  
  101.   les  di,[dword ptr es:di]                      { The first dword in the buffer for state information is the address - }
  102.                                                  { - of static funtionality table }
  103.   mov  cx,Mode                                   { Can only check for the 0h-13h modes }
  104.   cmp  cx,13h
  105.   ja   No_Support                                { Return 'no support' for modes > 13h }
  106.  
  107.   mov  ax,1                                      { Shift the right byte the right - }
  108.                                                  { - times and test for the right - }
  109.   cmp  cx,10h                                    { - bit for knowing if the       - }
  110.   jae  Last_Modes                                { - videomode is supported       - }
  111.                                                  { -                                }
  112.   shl  ax,cl                                     { -                                }
  113.   test ax,[word ptr es:di+0]                     { -                                }
  114.   jz   No_Support                                { -                                }
  115.   jmp  Supported                                 { -                                }
  116.                                                  { -                                }
  117. Last_Modes:                                      { -                                }
  118.   sub  cx,10h                                    { -                                }
  119.   shl  ax,cl                                     { -                                }
  120.   test al,[byte ptr es:di+2]                     { -                                }
  121.   jz   No_Support                                { -                                }
  122.  
  123. Supported:
  124.   mov  al,1                                      { AL=1 makes the function return TRUE }
  125.   jmp  Exit
  126.  
  127. No_Support:
  128.   mov  al,0                                      { AL=0 makes the function return TRUE }
  129.  
  130. Exit:
  131.   pop  es
  132. END;
  133.  
  134.  
  135. FUNCTION NoXMS:BOOLEAN; ASSEMBLER;
  136. { checks out if there is a XMS driver installed, and in case it initialize the
  137.   XMSEntryPoint Variable }
  138. LABEL JumpOver;
  139. ASM
  140.   push es
  141.  
  142.   mov  ax,4300h                                  { AX = 4300h => INSTALLATION CHECK }
  143.   int  2Fh                                       { use int 2Fh EXTENDED MEMORY SPECIFICATION (XMS) }
  144.   mov  bl,1                                      { use BL as a flag to indicate success }
  145.   cmp  al,80h                                    { is a XMS driver installed? }
  146.   jne  JumpOver
  147.   mov  ax,4310h                                  { AX = 4310h => GET DRIVER ADDRESS }
  148.   int  2Fh
  149.   mov  [word ptr XMSEntryPoint+0],BX             { initialize low word of XMSEntryPoint }
  150.   mov  [word ptr XMSEntryPoint+2],ES             { initialize high word of XMSEntryPoint }
  151.   mov  bl,0                                      { indicate success }
  152. JumpOver:
  153.   mov  al,bl                                     { make the function return TRUE (AH=1) or FALSE (AH=0) }
  154.  
  155.   pop  es
  156. END;
  157.  
  158.  
  159. FUNCTION XMSMaxAvail:WORD; ASSEMBLER;
  160. { returns size of largest contiguous block of XMS in kilo (1024) bytes }
  161. LABEL JumpOver;
  162. ASM
  163.   mov  ah,08h                                    { 'Query free extended memory' function }
  164.   mov  XMSError,0                                { clear error variable }
  165.   call [dword ptr XMSEntryPoint]
  166.   or   ax,ax                                     { check for error }
  167.   jnz  JumpOver
  168.   mov  XMSError,bl                               { errornumber stored in BL }
  169. JumpOver:                                        { AX=largest contiguous block of XMS }
  170. END;
  171.  
  172.  
  173. FUNCTION XMSGetMem(SizeInKB:WORD):WORD; ASSEMBLER;
  174. { allocates specified numbers of kilo (1024) bytes of XMS and return a handle
  175.   to this XMS block }
  176. LABEL JumpOver;
  177. ASM
  178.   mov  ah,09h                                    { 'Allocate extended memory block' function }
  179.   mov  dx,SizeInKB                               { number of KB requested }
  180.   mov  XMSError,0                                { clear error variable }
  181.   call [dword ptr XMSEntryPoint]
  182.   or   ax,ax                                     { check for error }
  183.   jnz  JumpOver
  184.   mov  XMSError,bl                               { errornumber stored in BL }
  185. JumpOver:
  186.   mov  ax,dx                                     { return handle number to XMS block }
  187. END;
  188.  
  189.  
  190. PROCEDURE XMSFreeMem(Handle:WORD); ASSEMBLER;
  191. LABEL JumpOver;
  192. ASM
  193.   mov  ah,0Ah                                    { 'Free extended memory block' function }
  194.   mov  dx,Handle                                 { XMS's handle number to free }
  195.   mov  XMSError,0                                { clear error variable }
  196.   call [dword ptr XMSEntryPoint]
  197.   or   ax,ax                                     { check for error }
  198.   jnz  JumpOver
  199.   mov  XMSError,bl                               { errornumber stored in BL }
  200. JumpOver:
  201. END;
  202.  
  203.  
  204. PROCEDURE XMSMove(VAR EMMParamBlock:EMMStructure); ASSEMBLER;
  205. LABEL JumpOver;
  206. ASM
  207.   push ds
  208.   push es
  209.   push ds
  210.   pop  es
  211.   mov  ah,0Bh                                    { 'Move extended memory block' function }
  212.   mov  XMSError,0                                { clear error variable }
  213.   lds  si,EMMParamBlock                          { DS:SI -> data to pass to the XMS routine }
  214.   call [dword ptr es:XMSEntryPoint]
  215.   or   ax,ax                                     { check for error }
  216.   jnz  JumpOver
  217.   mov  XMSError,bl                               { errornumber stored in BL }
  218. JumpOver:
  219.   pop  es
  220.   pop  ds
  221. END;
  222.  
  223.  
  224. PROCEDURE ExitDueToXMSError;
  225. BEGIN
  226.   InitMode(CO80);
  227.   WriteLn('ERROR! XMS routine has reported error ',XMSError);
  228.   XMSFreeMem(BufferHandle);
  229.   Halt(0);
  230. END;
  231.  
  232.  
  233. PROCEDURE GetBlock(VAR Buffer; Size:WORD);
  234. { reads a specified numbers of data from a diskfile or XMS into a buffer }
  235. VAR
  236.   XMSRecord             :EMMStructure;
  237.   NumberOfBytes         :WORD;
  238. BEGIN
  239.   IF UseXMS THEN
  240.   BEGIN
  241.     NumberOfBytes:=Size;
  242.     IF Size MOD 2=1 THEN
  243.       Inc(NumberOfBytes);  { one must allways ask for a EQUAL number of bytes }
  244.     WITH XMSRecord DO
  245.     BEGIN
  246.       BytesToMoveLo      :=NumberOfBytes;
  247.       BytesToMoveHi      :=0;
  248.       SourceHandle       :=BufferHandle;
  249.       SourceOffsetLo     :=FileCounter MOD 65536;
  250.       SourceOffsetHi     :=FileCounter DIV 65536;
  251.       DestinationHandle  :=0;
  252.       DestinationOffsetLo:=Ofs(Buffer);
  253.       DestinationOffsetHi:=Seg(Buffer);
  254.     END;
  255.     XMSMove(XMSRecord);
  256.     IF XMSError<>0 THEN
  257.       ExitDueToXMSError;
  258.     Inc(FileCounter,Size);
  259.   END
  260.   ELSE
  261.     BlockRead(InputFile,Buffer,Size);
  262. END;
  263.  
  264.  
  265. PROCEDURE InitClock; ASSEMBLER; {Taken from the FLILIB source}
  266. ASM
  267.   mov  al,00110100b                             { put it into linear count instead of divide by 2 }
  268.   out  CMODE,al
  269.   xor  al,al
  270.   out  CDATA,al
  271.   out  CDATA,al
  272. END;
  273.  
  274.  
  275. FUNCTION GetClock:LONGINT; ASSEMBLER; {Taken from the FLILIB source}
  276. { this routine returns a clock with occassional spikes where time
  277.   will look like its running backwards 1/18th of a second.  The resolution
  278.   of the clock is 1/(18*256) = 1/4608 second.  66 ticks of this clock
  279.   are supposed to be equal to a monitor 1/70 second tick.}
  280. ASM
  281.   mov  ah,0                                     { get tick count from dos and use for hi 3 bytes }
  282.   int  01ah                                     { lo order count in DX, hi order in CX }
  283.   mov  ah,dl
  284.   mov  dl,dh
  285.   mov  dh,cl
  286.  
  287.   mov  al,0                                 { read lo byte straight from timer chip }
  288.   out  CMODE,al                                     { latch count }
  289.   mov  al,1
  290.   out  CMODE,al                                     { set up to read count }
  291.   in   al,CDATA                                     { read in lo byte (and discard) }
  292.   in   al,CDATA                                     { hi byte into al }
  293.   neg  al                                     { make it so counting up instead of down }
  294. END;
  295.  
  296.  
  297. PROCEDURE TreatFrame(Buffer:POINTER;Chunks:WORD); ASSEMBLER;
  298. { this is the 'workhorse' routine that takes a frame and put it on the screen }
  299. { chunk by chunk }
  300. LABEL
  301.   Color_Loop, Copy_Bytes, Copy_Bytes2, Exit, Fli_Black, Fli_Brun, Fli_Color,
  302.   Fli_Copy, Fli_Lc, Fli_Loop, Jump_Over, Line_Loop, Line_Loop2, Next_Line,
  303.   Next_Line2, Pack_Loop, Pack_Loop2;
  304. ASM
  305.   cli                                            { disable interrupts }
  306.   push ds
  307.   push es                                        
  308.   lds  si,Buffer                                 { let DS:SI point at the frame to be drawn }
  309.  
  310. Fli_Loop:                                        { main loop that goes through all the chunks in a frame }
  311.   cmp  Chunks,0                                  { are there any more chunks to draw? }
  312.   je   Exit
  313.   dec  Chunks                                    { decrement Chunks for the chunk to process now }
  314.  
  315.   mov  ax,[word ptr ds:si+4]                     { let AX have the ChunkType }
  316.   add  si,6                                      { skip the ChunkHeader }
  317.  
  318.   cmp  ax,0Bh                                    { is it a FLI_COLOR chunk? }
  319.   je   Fli_Color
  320.   cmp  ax,0Ch                                    { is it a FLI_LC chunk? }
  321.   je   Fli_Lc
  322.   cmp  ax,0Dh                                    { is it a FLI_BLACK chunk? }
  323.   je   Fli_Black
  324.   cmp  ax,0Fh                                    { is it a FLI_BRUN chunk? }
  325.   je   Fli_Brun
  326.   cmp  ax,10h                                    { is it a FLI_COPY chunk? }
  327.   je   Fli_Copy
  328.   jmp  Fli_Loop                                  { This command should not be necessary since the program should make one - }
  329.                                                  { - of the other jumps }
  330.  
  331. Fli_Color:
  332.   mov  bx,[word ptr ds:si]                       { number of packets in this chunk (allways 1?) }
  333.   add  si,2                                      { skip the NumberOfPackets }
  334.   mov  al,0                                      { start at color 0 }
  335.   xor  cx,cx                                     { reset CX }
  336.  
  337. Color_Loop:
  338.   or   bx,bx                                     { set flags }
  339.   jz   Fli_Loop                                  { exit if no more packages }
  340.   dec  bx                                        { decrement NumberOfPackages for the package to process now }
  341.  
  342.   mov  cl,[byte ptr ds:si+0]                     { first byte in packet tells how many colors to skip }
  343.   add  al,cl                                     { add the skiped colors to the start to get the new start }
  344.   mov  dx,$3C8                                   { PEL Address Write Mode Register }
  345.   out  dx,al                                     { tell the VGA card what color we start changing }
  346.  
  347.   inc  dx                                        { at the port abow the PEL_A_W_M_R is the PEL Data Register }
  348.   mov  cl,[byte ptr ds:si+1]                     { next byte in packet tells how many colors to change }
  349.   or   cl,cl                                     { set the flags }
  350.   jnz  Jump_Over                                 { if NumbersToChange=0 then NumbersToChange=256 }
  351.   inc  ch                                        { CH=1 and CL=0 => CX=256 }
  352. Jump_Over:
  353.   add  al,cl                                     { update the color to start at }
  354.   mov  di,cx                                     { since each color is made of 3 bytes (Red, Green & Blue) we have to - }
  355.   shl  cx,1                                      { - multiply CX (the data counter) with 3 }
  356.   add  cx,di                                     { - CX = old_CX shl 1 + old_CX   (the fastest way to multiply with 3) }
  357.   add  si,2                                      { skip the NumbersToSkip and NumbersToChange bytes }
  358.   rep  outsb                                     { put the color data to the VGA card FAST! }
  359.  
  360.   jmp  Color_Loop                                { finish with this packet - jump back }
  361.  
  362.  
  363. Fli_Lc:
  364.   mov  ax,0A000h
  365.   mov  es,ax                                     { let ES point at the screen segment }
  366.   mov  di,[word ptr ds:si+0]                     { put LinesToSkip into DI - }
  367.   mov  ax,di                                     { - to get the offset address to this line we have to multiply with 320 - }
  368.   shl  ax,8                                      { - DI = old_DI shl 8 + old_DI shl 6 - }
  369.   shl  di,6                                      { - it is the same as DI = old_DI*256 + old_DI*64 = old_DI*320 - }
  370.   add  di,ax                                     { - but this way is faster than a plain mul }
  371.   mov  bx,[word ptr ds:si+2]                     { put LinesToChange into BX }
  372.   add  si,4                                      { skip the LinesToSkip and LinesToChange words }
  373.   xor  cx,cx                                     { reset cx }
  374.  
  375. Line_Loop:
  376.   or   bx,bx                                     { set flags }
  377.   jz  Fli_Loop                                   { exit if no more lines to change }
  378.   dec  bx
  379.  
  380.   mov  dl,[byte ptr ds:si]                       { put PacketsInLine into DL }
  381.   inc  si                                        { skip the PacketsInLine byte }
  382.   push di                                        { save the offset address of this line }
  383.  
  384. Pack_Loop:
  385.   or   dl,dl                                     { set flags }
  386.   jz   Next_Line                                 { exit if no more packets in this line }
  387.   dec  dl
  388.   mov  cl,[byte ptr ds:si+0]                     { put BytesToSkip into CL }
  389.   add  di,cx                                     { update the offset address }
  390.   mov  cl,[byte ptr ds:si+1]                     { put BytesOfDataToCome into CL }
  391.   or   cl,cl                                     { set flags }
  392.   jns  Copy_Bytes                                { no SIGN means that CL number of data is to come - }
  393.                                                  { - else the next data should be put -CL number of times }
  394.   mov  al,[byte ptr ds:si+2]                     { put the byte to be repeated into AL }
  395.   add  si,3                                      { skip the packet }
  396.   neg  cl                                        { repeat -CL times }
  397.   rep  stosb
  398.   jmp  Pack_Loop                                 { finish with this packet }
  399.  
  400. Copy_Bytes:                                      
  401.   add  si,2                                      { skip the two count bytes at the start of the packet }
  402.   rep  movsb
  403.   jmp  Pack_Loop                                 { finish with this packet }
  404.  
  405. Next_Line:
  406.   pop  di                                        { restore the old offset address of the current line }
  407.   add  di,320                                    { offset address to the next line }
  408.   jmp  Line_Loop
  409.  
  410.  
  411. Fli_Black:
  412.   mov  ax,0A000h
  413.   mov  es,ax                                     { let ES:DI point to the start of the screen }
  414.   xor  di,di
  415.   mov  cx,32000                                  { number of words in a screen }
  416.   xor  ax,ax                                     { color 0 is to be put on the screen }
  417.   rep  stosw
  418.   jmp  Fli_Loop                                  { jump back to main loop }
  419.  
  420.  
  421. Fli_Brun:
  422.   mov  ax,0A000h
  423.   mov  es,ax                                     { let ES:DI point at the start of the screen }
  424.   xor  di,di
  425.   mov  bx,200                                    { numbers of lines in a screen }
  426.   xor  cx,cx
  427.  
  428. Line_Loop2:
  429.   mov  dl,[byte ptr ds:si]                       { put PacketsInLine into DL }
  430.   inc  si                                        { skip the PacketsInLine byte }
  431.   push di                                        { save the offset address of this line }
  432.  
  433. Pack_Loop2:
  434.   or   dl,dl                                     { set flags }
  435.   jz   Next_Line2                                { exit if no more packets in this line }
  436.   dec  dl
  437.   mov  cl,[byte ptr ds:si]                       { put BytesOfDataToCome into CL }
  438.   or   cl,cl                                     { set flags }
  439.   js   Copy_Bytes2                               { SIGN meens that CL number of data is to come - }
  440.                                                  { - else the next data should be put -CL number of times }
  441.   mov  al,[byte ptr ds:si+1]                     { put the byte to be repeated into AL }
  442.   add  si,2                                      { skip the packet }
  443.   rep  stosb
  444.   jmp  Pack_Loop2                                { finish with this packet }
  445.  
  446. Copy_Bytes2:
  447.   inc  si                                        { skip the count byte at the start of the packet }
  448.   neg  cl                                        { repeat -CL times }
  449.   rep  movsb
  450.   jmp  Pack_Loop2                                { finish with this packet }
  451.  
  452. Next_Line2:
  453.   pop  di                                        { restore the old offset address of the current line }
  454.   add  di,320                                    { offset address to the next line }
  455.   dec  bx                                        { any more lines to draw? }
  456.   jnz  Line_Loop2
  457.   jmp  Fli_Loop                                  { jump back to main loop }
  458.  
  459.  
  460. Fli_Copy:
  461.   mov  ax,0A000h
  462.   mov  es,ax                                     { let ES:DI point to the start of the screen }
  463.   xor  di,di
  464.   mov  cx,32000                                  { number of words in a screen }
  465.   rep  movsw
  466.   jmp  Fli_Loop                                  { jump back to main loop }
  467.  
  468.  
  469. Exit:
  470.   sti                                            { enable interrupts }
  471.   pop  es
  472.   pop  ds
  473. END;
  474.  
  475.  
  476.  
  477. BEGIN
  478.   WriteLn;
  479.   WriteLn('.FLI-Player v1.1 by Thaco');
  480.   WriteLn('  (c) EPOS, August 1992');
  481.   WriteLn;
  482.   IF ParamCount=0 THEN                           { if no input parameters then write the 'usage text' }
  483.   BEGIN
  484.     WriteLn('USAGE: FLIPLAY <options> <filename>');
  485.     WriteLn('                   '+#24+'         '+#24);
  486.     WriteLn('                   │         └──  Filename of .FLI file');
  487.     WriteLn('                   └────────────  -d   = Do not use XMS');
  488.     WriteLn('                                  -i   = Information about the program');
  489.     WriteLn('                                  -n   = No checking of MCGA mode support');
  490.     WriteLn('                                  -sNN = Set playspeed to NN video ticks (0-99)');
  491.     WriteLn('                                         ( NN=70 ≈ frame delay of 1 second )');
  492.     Halt(0);
  493.   END;
  494.  
  495.   FOR Counter:=1 TO ParamCount DO                { search through the input parameters for a -Info option }
  496.     IF Pos('-I',UpCaseString(ParamStr(Counter)))<>0 THEN
  497.     BEGIN
  498.       WriteLn('Program information:');
  499.       WriteLn('This program plays animations (sequences of pictures) made by programs like',#10#13,
  500.               'Autodesk Animator (so called .FLI-files). The program decodes the .FLI file,',#10#13,
  501.               'frame by frame, and uses the systemclock for mesuring the time-delay between',#10#13,
  502.               'each frame.');
  503.       WriteLn('Basis for the program was the FliLib package made by Jim Kent, but since the',#10#13,
  504.               'original source was written in C, and I am not a good C-writer, I decided',#10#13,
  505.               'to write my own .FLI-player in Turbo Pascal v6.0.');
  506.       WriteLn('This program was made by Eirik Milch Pedersen (thaco@solan.unit.no).');
  507.       WriteLn('Copyright Eirik Pedersens Own Softwarecompany (EPOS), August 1992');
  508.       WriteLn;
  509.       WriteLn('Autodesk Animator is (c) Autodesk Inc');
  510.       WriteLn('FliLib is (c) Dancing Flame');
  511.       WriteLn('Turbo Pascal is (c) Borland International Inc');
  512.       Halt(0);
  513.     END;
  514.  
  515.   Speed:=-1;
  516.   Counter:=1;
  517.   WHILE (Copy(ParamStr(Counter),1,1)='-') AND (ParamCount>=Counter) DO { search through the input parameters to assemble them }
  518.   BEGIN
  519.    IF Pos('-D',UpCaseString(ParamStr(Counter)))<>0 THEN  { do not use XMS for storing the file into memory }
  520.      UseXMS:=FALSE
  521.    ELSE
  522.      IF Pos('-N',UpCaseString(ParamStr(Counter)))<>0 THEN  { do not check for a vga card present }
  523.        MCGACheck:=FALSE
  524.      ELSE
  525.        IF Pos('-S',UpCaseString(ParamStr(Counter)))<>0 THEN { speed override has been specified }
  526.        BEGIN
  527.          SpeedString:=Copy(ParamStr(Counter),3,2);  { cut out the NN parameter }
  528.          IF NOT(SpeedString[1] IN ['0'..'9']) OR    { check if the NN parameter is legal }
  529.             (NOT(SpeedString[2] IN ['0'..'9',' ']) AND (Length(SpeedString)=2)) THEN
  530.          BEGIN
  531.            WriteLn('ERROR! Can not parse speed ''',SpeedString,'''.');
  532.            Halt(0);
  533.          END;
  534.          Speed:=Byte(SpeedString[1])-48;  { take the first number, in ASCII, and convert it to a standard number }
  535.          IF Length(SpeedString)=2 THEN    { if there is two numbers then multiply the first with 10 and add the next }
  536.            Speed:=Speed*10+Byte(SpeedString[2])-48;
  537.          Speed:=Speed*CLOCK_SCALE;        { convert the speed to number of clock tics }
  538.        END;
  539.    Inc(Counter);
  540.   END;
  541.  
  542.   IF ParamCount<Counter THEN
  543.   BEGIN
  544.     WriteLn('ERROR! No filename specified.');
  545.     Halt(0);
  546.   END;
  547.  
  548.   FileName:=UpCaseString(ParamStr(Counter));
  549.   IF Pos('.',FileName)=0 THEN  { find out if there exist a . in the filename }
  550.     FileName:=FileName+'.FLI'; { if not then add the .FLI extension on the filename }
  551.  
  552.   IF MaxAvail<BUFFERSIZE THEN   { check if there is enough memory to the frame buffer }
  553.   BEGIN
  554.     WriteLn('ERROR! Can not allocate enough memory to a frame buffer.');
  555.     Halt(0);
  556.   END;
  557.  
  558.   GetMem(Buffer,BUFFERSIZE);
  559.   Assign(InputFile,FileName);
  560.   Reset(InputFile,1);
  561.   IF IOResult<>0 THEN  { has an error occured during opening the file? }
  562.   BEGIN
  563.     WriteLn('ERROR! Can not open file ''',FileName,'''.');
  564.     Halt(0);
  565.   END;
  566.  
  567.   IF NOT(MCGACheck) OR ModeSupport(MCGA) THEN
  568.     InitMode(MCGA)
  569.   ELSE
  570.   BEGIN
  571.     WriteLn('ERROR! Video mode 013h - 320x200x256 colors - is not supported.');
  572.     Halt(0);
  573.   END;
  574.  
  575.   BlockRead(InputFile,Header,128);  { read the .FLI main header }
  576.  
  577.   IF NOT((Header[4]=$11) AND (Header[5]=$AF)) THEN  { check if the file has got the magic number }
  578.   BEGIN
  579.     InitMode(CO80);
  580.     WriteLn('ERROR! File ''',FileName,''' is of a wrong file type.');
  581.     Halt(0);
  582.   END;
  583.  
  584.   IF NoXMS THEN  { if no XMS driver present then do not use XMS }
  585.     UseXMS:=FALSE;
  586.  
  587.   IF UseXMS THEN
  588.   BEGIN
  589.     FileSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt(Header[3])));
  590.     IF XMSMaxAvail<=(FileSize+1023) SHR 10 THEN  { is there enough XMS (rounded up to nearest KB) availible? }
  591.     BEGIN
  592.       WriteLn('ERROR! Not enough XMS for the file');
  593.       Halt(0);
  594.     END
  595.     ELSE
  596.     BEGIN
  597.       Seek(InputFile,0);  { skip back to start of .FLI-file to put it all into XMS }
  598.       BufferHandle:=XMSGetMem((FileSize+1023) SHR 10);  { allocate XMS for the whole .FLI file }
  599.       FileCounter:=0;
  600.       REPEAT
  601.         BlockRead(InputFile,Buffer^,BUFFERSIZE,BytesRead);  { read a part from the .FLI file }
  602.         IF BytesRead MOD 2=1 THEN  { since BUFFERSIZE shoud be an even number, the only time this triggers is the last part }
  603.           Inc(BytesRead);          { must be done because the XMS routine demands an even number of bytes to be moved }
  604.         IF BytesRead<>0 THEN
  605.         BEGIN
  606.           WITH XMSRecord DO  { put data into the XMSRecord }
  607.           BEGIN
  608.             BytesToMoveLo      :=BytesRead;
  609.             BytesToMoveHi      :=0;
  610.             SourceHandle       :=0;
  611.             SourceOffsetLo     :=Ofs(Buffer^);
  612.             SourceOffsetHi     :=Seg(Buffer^);
  613.             DestinationHandle  :=BufferHandle;
  614.             DestinationOffsetLo:=FileCounter MOD 65536;
  615.             DestinationOffsetHi:=FileCounter DIV 65536;
  616.           END;
  617.           XMSMove(XMSRecord);   { move bytes to XMS }
  618.           IF XMSError<>0 THEN   { have any XMS errors occured? }
  619.             ExitDueToXMSError;
  620.           Inc(FileCounter,BytesRead);  { update the offset into XMS where to put the next bytes }
  621.         END;
  622.       UNTIL BytesRead<>BUFFERSIZE;  { repeat until bytes read <> bytes tried to read => end of file }
  623.     END;
  624.     FileCounter:=128;  { we continue (after reading the .FLI file into XMS) right after the .FLI main header }
  625.   END;
  626.  
  627.   Frames:=Header[6]+Header[7]*256;  { get the number of frames from the .FLI-header }
  628.   IF Speed=-1 THEN                  { if speed is not set by a speed override then get it from the .FLI-header }
  629.     Speed:=(Header[16]+Integer(Header[17])*256)*CLOCK_SCALE;
  630.   InitClock;  { initialize the System Clock }
  631.   OldKey:=PORT[$60];  { get the current value from the keyboard }
  632.   Key:=OldKey;        { and set the 'current key' Variable to the same value }
  633.  
  634.   GetBlock(Header,16);  { read the first frame-header }
  635.   FrameSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt(Header[3])))-16;  { calculate framesize }
  636.   SecondPos:=128+16+FrameSize;  { calculate what position to skip to when the .FLI is finished and is going to start again - }
  637.                                 { the position = .FLI-header + first_frame-header + first_framesize }
  638.   Chunks:=Header[6]+Header[7]*256;  { calculate number of chunks in frame }
  639.   GetBlock(Buffer^,FrameSize);  { read the frame into the framebuffer }
  640.   TreatFrame(Buffer,Chunks);  { treat the first frame }
  641.  
  642.   TimeCounter:=GetClock;  { get the current time }
  643.  
  644.   {
  645.     The first frame must be handeled separatly from the rest. This is because the rest of the frames are updates/changes of the
  646.     first frame.
  647.     At the end of the .FLI-file there is one extra frame who handles the changes from the last frame to the first frame.
  648.   }
  649.  
  650.   REPEAT
  651.     FrameNumber:=1;  { we start at the first frame (after the initial frame) }
  652.     REPEAT
  653.       GetBlock(Header,16);  { read frame-header }
  654.       FrameSize:=Header[0]+256*(LongInt(Header[1])+256*(LongInt(Header[2])+256*LongInt(Header[3])))-16;  { size of frame }
  655.       IF FrameSize<>0 THEN  { sometimes there are no changes from one frame to the next (used for extra delays). In such - }
  656.                             { - cases the size of the frame is 0 and we don't have to process them }
  657.       BEGIN
  658.         Chunks:=Header[6]+Header[7]*256;  { calculate number of chunks in the frame }
  659.         GetBlock(Buffer^,FrameSize);  { read the frame into the framebuffer }
  660.         TreatFrame(Buffer,Chunks);  { treat the frame }
  661.       END;
  662.  
  663.       NextTime:=TimeCounter+Speed;   { calculate the delay to the next frame }
  664.       WHILE TimeCounter<NextTime DO  { wait for this long }
  665.         TimeCounter:=GetClock;
  666.  
  667.       IF PORT[$64]=KEYBOARD THEN   { check if the value at the keyboard port is caused by a key pressed }
  668.         Key:=PORT[$60];            { get the current value from the keyboard }
  669.       Inc(FrameNumber);  { one frame finished, over to the next one }
  670.     UNTIL (FrameNumber>Frames) OR (Key<>OldKey);  { repeated until we come to the last frame or a key is pressed }
  671.  
  672.     IF UseXMS THEN
  673.       FileCounter:=SecondPos
  674.     ELSE
  675.       Seek(InputFile,SecondPos);  { set current position in the file to the second frame }
  676.  
  677.   UNTIL Key<>OldKey;  { exit the loop if a key has been pressed }
  678.  
  679.   InitMode(CO80);  { get back to text mode }
  680.  
  681.   Close(InputFile);            { be a kind boy and close the file before we end the program }
  682.   FreeMem(Buffer,BUFFERSIZE);  { and free the framebuffer }
  683.  
  684.   IF UseXMS THEN
  685.     XMSFreeMem(BufferHandle);
  686. END.